import Channel from "../channel/channel";
import { createConnection, createServer, Socket } from "net";
import AvoidNullCallable from "../avoidnullcall/avoidnullcallable";
import Control from "../interfaces/control";
import { dispatcher_log } from "../log/logger";

interface bound {
    [index: string]: client
}
interface client {
    _client: Socket,
    referPort: number,
}
export default class Dispatcher extends AvoidNullCallable
{
    private tunnelN: number;

    private channel: Channel;       //数据通道
    private controller: Control;    //控制通道

    private bounds: bound[];

    private tunnel_allow_sendData: boolean;

    private pause_bounds: number[];

    private options: any;

    /**
     * 构造函数
     * @param options 根据type定义的监听配置或连接配置
     * @param controller 控制通道配件
     * @param tunnelN 数据通道数
     * @param type dispatcher类型：true服务器，false客户端
     */
    constructor(
        options: any,
        options2: any,
        controller: Control,
        tunnelN: number,
        type: boolean
    )
    {
        super();

        this.tunnelN = tunnelN;
        this.controller = controller;
        this.bounds = new Array<bound>();
        this.pause_bounds = new Array<number>();
        this.options = options;
        this.options.allowHalfOpen = true;   

        if(!type)
        {
            //客户端
            let local_server = createServer({
                allowHalfOpen: true,
                pauseOnConnect: true
            }).listen(options, () => {
                dispatcher_log("正在监听" + options.port + "端口");
            });

            local_server.on("connection", (socket: Socket) => {
                let newClient: client = {
                    _client: socket,
                    referPort: socket.remotePort,
                };

                if(this.bounds[newClient.referPort.toString()] != undefined)
                {
                    dispatcher_log("连接池中出现未曾释放的端口" + newClient.referPort);
                }

                socket.on("close", () => {
                    this.channel.clearPort(newClient.referPort);
                    this.closePort(newClient.referPort.toString());
                });

                this.initSocketData(newClient);
                //this.controller.pushControlDataToRemote("client_port_open", [newClient.referPort]);
                this.channel.sendData(Buffer.from("COPEN"), newClient.referPort);
            });

            this.controller.handleConnectionSuccess(() => {
                this.controller.pushControlDataToRemote("tunnel_open", [this.tunnelN]);
            });
        }else
        {
            //服务器
            this.controller.handleConnectionSuccess(() => {});
        }

        this.controller.registerControlCommand("server_port_close", (args: number[]) => {
            //this.closePort(args[0].toString());
        }).registerControlCommand("tunnel_ok", (args: number[]) => {
            this.channel = new Channel(tunnelN, false, false, {host: options2.host, port: args[0]}, () => {});

            this.channel.regChannelAllConnected(() => {
                console.log("多通道数据已连接");
                this.tunnel_allow_sendData = true;
            });

            this.initChannel();
        }).registerControlCommand("server_port_open", (args: number[]) => {
        }).registerControlCommand("client_port_close", (args: number[]) => {
            //this.closePort(args[0].toString());
        }).registerControlCommand("client_port_open", (args: number[]) => {

            //.on("close", () => {
                //this.closePort(args[0].toString());
                //this.controller.pushControlDataToRemote("server_port_close", [args[0]]);

                //console.log(args[0], "连接关闭");
            //});
        }).registerControlCommand("tunnel_open", (args: number[]) => {
            console.log("接收到通道开通请求，参数:" + args[0]);

            this.channel = new Channel(args[0], false, true, options2, () => {
                this.tunnel_allow_sendData = true;
                this.controller.pushControlDataToRemote("tunnel_ok", [options2.port]);
            });

            this.channel.regChannelAllConnected(() => {
                console.log("客户端数据通道已连接");
            });

            this.initChannel();
        }).registerControlCommand("port_limit", (args: number[]) => {
            //console.log("收到节流请求，端口", args[0]);
            //this.pausePort(args[0].toString());
        }).registerControlCommand("port_stop_limit", (args: number[]) => {
            //console.log("收到停止节流请求，端口", args[0]);
            //this.resumePort(args[0].toString());
        }).registerControlCommand("port_fin", (args: number[]) => {
            //this.relayFinPacket(args[0].toString());
        });

            
    }

    private closePort(port: string)
    {
        if(this.bounds[port] == undefined) return;

        this.bounds[port]._client.destroy();
        this.bounds[port] = undefined;
    }

    private relayFinPacket(port: string)
    {
        if(this.bounds[port] == undefined) return;

        this.bounds[port]._client.end();
    }

    private resumePort(port: string)
    {
        if(this.bounds[port] == undefined) return;

        this.bounds[port]._client.resume();
    }

    private pausePort(port: string)
    {
        if(this.bounds[port] == undefined) return;

        this.bounds[port]._client.pause();
    }

    private initChannel()
    {
        this.channel.regDataRecive((referPort: number, data: Buffer) => {
            if(data.length == 5)
            {
                let cmd = data.toString();
                if(cmd == "PTCLS")
                {
                    //console.log("PTCLS");
                    this.OnRemoteReportFinPacketRecived(referPort);
                }else if(cmd == "COPEN")
                {
                    this.OnClientReportPortOpened(referPort);
                }else if(cmd == "SOPEN")
                {
                    this.OnServerReportPortOpened(referPort);
                }else if(cmd == "PTSTP")
                {
                    this.pausePort(referPort.toString());
                }else if(cmd == "PTCTN")
                {
                    this.resumePort(referPort.toString());
                }
                return;
            }

            if(this.bounds[referPort.toString()] != undefined)
            {
                if(!this.bounds[referPort.toString()]._client.write(data))
                {
                    console.log(referPort, "输出到外部需要节流");
                    //this.bounds[referPort.toString()]._client.pause();
                    this.channel.pausePort(referPort);
                    //this.channel.pause();
                    //this.controller.pushControlDataToRemote("port_limit", [referPort]);
                }
            }
        });
        this.channel.regChannelDrain(() => {
            //this.channel.resume();
            while(true)
            {
                let pause_port = this.pause_bounds.shift();
                if(pause_port == undefined) return;

                if(this.bounds[pause_port.toString()] != undefined)
                {
                    this.bounds[pause_port.toString()]._client.resume();

                    //console.log(pause_port, "取消节流")
                }
            }
        });

        this.channel.regPortChoke((port: number) => {
            this.channel.sendData(Buffer.from("PTSTP"), port);
            console.log(port, "包数量达到最大可支撑数量，请求暂停传输");
        }).regPortUnChoked((port: number) => {
            this.channel.sendData(Buffer.from("PTCTN"), port);
            console.log(port, "包数据已经流完，请求继续传输");
        });
    }
    
    private initSocketData(client: client)
    {
        client._client.on("data", (data:Buffer) => {
            if(this.tunnel_allow_sendData)
            {
                //console.log(client.referPort, data);
                if(!this.channel.sendData(data, client.referPort))
                {
                    //console.log(client.referPort, "需要节流");

                    this.pausePort(client.referPort.toString());
                    this.pause_bounds.push(client.referPort);
                }
            }
        }).on("drain", () => {
            console.log(client.referPort, "外部节流结束");
            //this.channel.resume();
            this.channel.resumePort(client.referPort);
            //this.controller.pushControlDataToRemote("port_stop_limit", [client.referPort]);
            //this.bounds[client.referPort.toString()]._client.resume();
        }).on("error", (err: Error) => {
            this.channel.clearPort(client.referPort);
            this.closePort(client.referPort.toString());
            console.log(err);
        }).on("end", () => {
            //this.controller.pushControlDataToRemote("port_fin", [client.referPort]);
            this.channel.sendData(Buffer.from("PTCLS"), client.referPort);

            setTimeout(() => {
                this.closePort(client.referPort.toString());
            }, 60* 1000);
        })

        this.bounds[client.referPort.toString()] = client;

    }

    private OnRemoteReportFinPacketRecived(port: number) 
    {
        this.relayFinPacket(port.toString());
        setTimeout(() => {
            this.closePort(port.toString());
        }, 60 * 1000);
    }

    private OnClientReportPortOpened(port: number)
    {
        let client = createConnection(this.options, () => {
            //this.controller.pushControlDataToRemote("server_port_open", [rgs[0]]);
            this.channel.sendData(Buffer.from("SOPEN"), port);

            let newClient:client = {
                referPort: port,
                _client: client,
            };

            this.initSocketData(newClient);
        }).on("close", () => {
            //this.channel.resumePort(port);
            this.channel.clearPort(port);
            this.closePort(port.toString());
        })
    }

    private OnServerReportPortOpened(port: number)
    {
        this.resumePort(port.toString());
    }
}